﻿// /////////////////////////////////// 
//  
//  
//      WiseLink CCF2.0 
//     (c)Copyright 2001-2010 WiseLink Xiaojun (David) Chen 
//        Authored and Owned Xiaojun (David) Chen 
//        All Right Reserved
//  
// /////////////////////////////////// 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZIC20Contract.DataType;
using Infrastructure.Log;
using Infrastructure.Extension;
using System.Runtime.InteropServices;

namespace ZICModel.ZISMessage
{
    public class Message
    {
        private string m_Head = "7E"; //帧头(0X7E)
        private string m_ControlByte = "00"; //控制(0X00)
        private string m_FrameID = "05"; //ID号
        private string m_CommandByte ="45"; //命令字 0x45

        private string m_DataLength; //长度(1+N)从信息长度后一个字节开始，到效验和前面的一个字节介结束
        private int dataLength;

        private string m_msgBody = string.Empty; //用户使用，具体的数据内容，信息内容（N）
        private string m_msgHeader; //信息类型

        private string m_CheckSum = string.Empty; //帧头后至效验和前的字节异或和
        private string m_Tail = "7F"; //0x7F
        private string m_idc; //
        private ZISMsgType m_zisMsgType;
        
        private GPRSMsgType m_gprsMsgType;

        private POINavigation m_POI;
        private string m_fullBody;

        public Message(string idc, ZISMsgType zisMsgType, GPRSMsgType gprsType, string msg)
        {
            m_idc = idc;
            m_zisMsgType = zisMsgType;
            m_gprsMsgType = gprsType;
            m_msgBody = msg;
        }


        public Message(string idc, ZISMsgType zisMsgType, GPRSMsgType gprsType, POINavigation msg)
        {
            m_idc = idc;
            m_zisMsgType = zisMsgType;
            m_gprsMsgType = gprsType;
            
            m_POI.fLat = msg.fLat;
            m_POI.fLng = msg.fLng;
            m_POI.ils_Encrypt = msg.ils_Encrypt;
            m_POI.wcCall_Phone = msg.wcCall_Phone;
            m_POI.wcCreate_Time = msg.wcCreate_Time;
            m_POI.wcName = msg.wcName;

        }

        private byte[] SwapByteValue(byte[] input)
        {
            byte tempByte;
            for (int i = 0; i < input.Length; i += 2)
            {
                tempByte = input[i];
                input[i] = input[i + 1];
                input[i + 1] = tempByte;
            }
            return input;
        }

        //BigEndian Unicode Message Body
        private string SetMessageBody()
        {
            //convert to unicode with the default endian, for debug purpose.

            byte[] source = null;
            if (!m_msgBody.Equals(string.Empty))
            {
                // this is GPRS normal message
                source = Encoding.Unicode.GetBytes(m_msgBody);
            }
            else
            {
                // this is POI message, that we need to use the POI Structre
                source = StructToBytes(m_POI);
            }
            string strSource = string.Empty;
            source.ToList().ForEach((b) => strSource = string.Concat(strSource,b.ToString("X").PadLeft(2,'0')+"-"));

            byte[] target = null;

            if (!m_msgBody.Equals(string.Empty))
            {
                // this is GPRS normal message
                UnicodeEncoding unicode = new UnicodeEncoding(true, false); //IsBigEndian = true, byteOrderMark = false
                target = unicode.GetBytes(m_msgBody);
            }
            else
            {
                // this is POI message, that we need to use the POI Structre
                target = SwapByteValue(source);
            }

            string strTarget = string.Empty;
            target.ToList().ForEach((b) => strTarget = string.Concat(strTarget, b.ToString("X").PadLeft(2,'0') + "-"));

            

            CallLogger.LogMessage(typeof(Message), string.Format("Source internal msg body: {0}", strSource));
            CallLogger.LogMessage(typeof(Message), string.Format("Target internal msg body: {0}", strTarget));
            
            m_msgBody = strTarget.Replace("-",string.Empty);
            return m_msgBody;
        }

        private int SetMessageLength()
        {
            //length is the count of byte of msg body+msg header
            //in hex, every character is 4 bit, so every byte (8 bit) can hold 2 characters.
            // for instance 'a' = 97 (based on 10) =61 (basec on 16). 61 is 2 characters but they will be put in one byte 01100001
            // so we divide the count of char in the converted hex string and +1 for the message header.
            dataLength = m_msgBody.Length / 2 + 1;
            m_DataLength = dataLength.ToString("X").PadLeft(4, '0');
            CallLogger.LogMessage(typeof(Message), string.Format("Internal msg length (include internal msg header: Decimal={0} Hex={1}",dataLength, m_DataLength));
            return dataLength;
        }

        private void SetCheckSum()
        {
            //帧头后至效验和前的字节异或和
            string msg = string.Format("{0}{1}{2}{3}{4}{5}", m_ControlByte, m_FrameID, m_CommandByte, m_DataLength, m_msgHeader, m_msgBody);
            CallLogger.LogMessage(typeof(Message), string.Format("The string used to caculate checkSum = {0}", msg));
            int checkSum = Convert.ToInt32(msg.Substring(0, 2),16);
            for (int i = 2; i< msg.Length-1; i=i+2)
            {
                checkSum = checkSum ^ Convert.ToInt32(msg.Substring(i, 2),16);
            }
            m_CheckSum = checkSum.ToString("X").PadLeft(2, '0');
            CallLogger.LogMessage(typeof(Message), string.Format("CheckSum: Decimal = {0}, Hex = {1}", checkSum,m_CheckSum));
        }

        private void SetMessagerHeader()
        {
            m_msgHeader = ((int)m_gprsMsgType).ToString("X").PadLeft(2, '0');
            CallLogger.LogMessage(typeof(Message), string.Format("Internal MsgType = {0}", m_msgHeader));
        }

        

        public void Process()
        {
            SetMessagerHeader();
            SetMessageBody();
            SetMessageLength();
            SetCheckSum();
            Serialize();
        }

        private void Serialize()
        {
            m_fullBody = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}{8}", m_Head, m_ControlByte, m_FrameID, m_CommandByte, m_DataLength, m_msgHeader, m_msgBody, m_CheckSum, m_Tail);
            
            
            m_fullBody = m_fullBody.Replace("7d", "7d5d").Replace("7f", "7d5f").Replace("7e", "7d5e").ToUpper();
            
            string debugStr = string.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}-{7}-{8}", m_Head, m_ControlByte, m_FrameID, m_CommandByte, m_DataLength, m_msgHeader, m_msgBody, m_CheckSum, m_Tail);
            debugStr = debugStr.Replace("7d", "7d5d").Replace("7f", "7d5f").Replace("7e", "7d5e").ToUpper();

            string debugFormat = string.Format("Serialized Msg Format = Header-ControlByte-FrameID-CommandByte-MsgLength-MsgType-MsgBody-CheckSum-Tail");
            CallLogger.LogMessage(typeof(Message), debugFormat);
            CallLogger.LogMessage(typeof(Message), string.Format("Serialized Full Message = {0}", debugStr));
        }

        
        private byte[] StructToBytes(POINavigation structure)
        {
            byte[] destination = null;
            int size = Marshal.SizeOf(structure);

            IntPtr srcStructure = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr(structure, srcStructure, false);
                destination = new byte[size];
                Marshal.Copy(srcStructure, destination, 0, size);
                
            }
            catch (Exception ex)
            {
                CallLogger.LogMessage(typeof(Message), string.Format("StructureToByteException: {0} \r\n \t\t {1}", ex.Message,ex.StackTrace));
            }
            finally
            {
                Marshal.FreeHGlobal(srcStructure);
            }
            return destination;
        }
         
#region property group

        public string FormatedMessage
        {
            get
            {
                return m_fullBody;
            }
        }

      
#endregion property group
    }
}
